﻿// Decompiled with JetBrains decompiler
// Type: Unity.Netick.CodeGen.PostProcessorAssemblyResolver
// Assembly: Unity.Netick.CodeGen, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: 8B3B8749-9F65-48DA-BE6F-84634E89AC85
// Assembly location: D:\WorkSpaceDemo\UPM\NetickForUnity\Netick\Editor\Unity.Netick.CodeGen.dll

using Mono.Cecil;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using Unity.CompilationPipeline.Common.ILPostProcessing;

#nullable disable
namespace Unity.Netick.CodeGen;

internal class PostProcessorAssemblyResolver : IAssemblyResolver, IDisposable
{
  private readonly string[] m_AssemblyReferences;
  private readonly Dictionary<string, AssemblyDefinition> m_AssemblyCache = new Dictionary<string, AssemblyDefinition>();
  private readonly ICompiledAssembly m_CompiledAssembly;
  private AssemblyDefinition m_SelfAssembly;

  public PostProcessorAssemblyResolver(ICompiledAssembly compiledAssembly)
  {
    this.m_CompiledAssembly = compiledAssembly;
    this.m_AssemblyReferences = compiledAssembly.References;
  }

  public void Dispose()
  {
  }

  public AssemblyDefinition Resolve(AssemblyNameReference name)
  {
    return this.Resolve(name, new ReaderParameters((ReadingMode) 2));
  }

  public AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters)
  {
    lock (this.m_AssemblyCache)
    {
      if (name.Name == this.m_CompiledAssembly.Name)
        return this.m_SelfAssembly;
      string file = this.FindFile(name);
      if (file == null)
        return (AssemblyDefinition) null;
      DateTime lastWriteTime = File.GetLastWriteTime(file);
      string key = $"{file}{lastWriteTime}";
      AssemblyDefinition assemblyDefinition1;
      if (this.m_AssemblyCache.TryGetValue(key, out assemblyDefinition1))
        return assemblyDefinition1;
      parameters.AssemblyResolver = (IAssemblyResolver) this;
      MemoryStream memoryStream = PostProcessorAssemblyResolver.MemoryStreamFor(file);
      string str = file + ".pdb";
      if (File.Exists(str))
        parameters.SymbolStream = (Stream) PostProcessorAssemblyResolver.MemoryStreamFor(str);
      ReaderParameters readerParameters = parameters;
      AssemblyDefinition assemblyDefinition2 = AssemblyDefinition.ReadAssembly((Stream) memoryStream, readerParameters);
      this.m_AssemblyCache.Add(key, assemblyDefinition2);
      return assemblyDefinition2;
    }
  }

  private string FindFile(AssemblyNameReference name)
  {
    return ((IEnumerable<string>) this.m_AssemblyReferences).FirstOrDefault<string>((Func<string, bool>) (r => Path.GetFileName(r) == name.Name + ".dll")) ?? ((IEnumerable<string>) this.m_AssemblyReferences).FirstOrDefault<string>((Func<string, bool>) (r => Path.GetFileName(r) == name.Name + ".exe")) ?? ((IEnumerable<string>) this.m_AssemblyReferences).Select<string, string>(new Func<string, string>(Path.GetDirectoryName)).Distinct<string>().Select<string, string>((Func<string, string>) (parentDir => Path.Combine(parentDir, name.Name + ".dll"))).FirstOrDefault<string>(new Func<string, bool>(File.Exists));
  }

  private static MemoryStream MemoryStreamFor(string fileName)
  {
    return PostProcessorAssemblyResolver.Retry(10, TimeSpan.FromSeconds(1.0), (Func<MemoryStream>) (() =>
    {
      using (FileStream fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
      {
        byte[] buffer = new byte[fileStream.Length];
        if ((long) fileStream.Read(buffer, 0, (int) fileStream.Length) != fileStream.Length)
          throw new InvalidOperationException("File read length is not full length of file.");
        return new MemoryStream(buffer);
      }
    }));
  }

  private static MemoryStream Retry(int retryCount, TimeSpan waitTime, Func<MemoryStream> func)
  {
    try
    {
      return func();
    }
    catch (IOException ex)
    {
      if (retryCount == 0)
        throw;
      Console.WriteLine($"Caught IO Exception, trying {retryCount} more times");
      Thread.Sleep(waitTime);
      return PostProcessorAssemblyResolver.Retry(retryCount - 1, waitTime, func);
    }
  }

  public void AddAssemblyDefinitionBeingOperatedOn(AssemblyDefinition assemblyDefinition)
  {
    this.m_SelfAssembly = assemblyDefinition;
  }
}
